import static org.junit.Assert.*;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import jdtprototype.JdtModel;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.epsilon.eol.EolModule;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.internal.core.PackageFragment;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

import util.Visitor;


public class JdtDriverTest {
	static IProject[] projects;
	static IJavaProject [] javaProjects;
	static IJavaProject javaProject;
	static IPackageFragment packageFragment;
	static CompilationUnit unit;
	static TypeDeclaration typeDeclaration;
	static MethodDeclaration methodDeclaration;
	
	static EolModule module = new EolModule();
	static JdtModel model = new JdtModel();
	

	/**
	 * Initialises instances variables which will be used as sample parameters
	 * @throws Exception
	 */
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IWorkspaceRoot root = workspace.getRoot();	
		//get all projects in the workspace
		projects = root.getProjects();
		All: for(IProject project : projects){
			if(project.isNatureEnabled("org.eclipse.jdt.core.javanature")&&project.isOpen()){
				javaProject=JavaCore.create(project);

				IPackageFragment[] packages = javaProject.getPackageFragments();
				for (IPackageFragment tempPackageFragment : packages) {
					if (tempPackageFragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
						//initialise packageFragment
						packageFragment=tempPackageFragment;
						ICompilationUnit [] iUnits = packageFragment.getCompilationUnits();
						for(ICompilationUnit iUnit : iUnits){
							// initialise unit
							unit = Visitor.parse(iUnit);
							Visitor cVisitor = new Visitor();
							unit.accept(cVisitor);
							TypeDeclaration[] typeDeclarations = cVisitor
									.getClasses().toArray(
											new TypeDeclaration[cVisitor
													.getClasses().size()]);
							for (TypeDeclaration tempTypeDeclaration : typeDeclarations) {
								// initialise typeDeclaration
								typeDeclaration = tempTypeDeclaration;
								MethodDeclaration[] methodDeclarations = typeDeclaration
										.getMethods();
								for(MethodDeclaration tempMethod : methodDeclarations){
									// initialise methodDeclaration
									methodDeclaration = tempMethod;
									
									break All;
								}
							}
						}
					}
				}
			}					
		}
		
		List<IJavaProject> javaProjectList = new ArrayList<IJavaProject>();
		for(IProject project : projects){
			if(project.isNatureEnabled("org.eclipse.jdt.core.javanature")&&project.isOpen()){
				javaProjectList.add(JavaCore.create(project));
			}
		}
		javaProjects = javaProjectList.toArray(new IJavaProject [javaProjectList.size()]);
		
		model.setName("JDTModel");
		model.load();
		
		module.getContext().getModelRepository().addModel(model);
	}

	/**
	 * Tests JdtModel.getAllOfType()
	 * @throws Exception
	 */
	@Test @Ignore
	public void testGetAllOfType() throws Exception{
//		module.parse("traverse (); "
//				+ "operation Any traverse () : Integer{"
//				+ "for (p in JavaProject.all) {" 
//				+ "p.println();}"
//				+ "return 0;}");
//		module.execute();

		IJavaProject [] actualIJavaProjects = (model.getAllOfType("JavaProject")).toArray(new IJavaProject [model.getAllOfType("JavaProject").size()]);
		assertArrayEquals(javaProjects, actualIJavaProjects);
	}
	
	/**
	 * Tests JdtPropertyGetter.invoke(javaProject, "package")
	 * @throws EolRuntimeException 
	 */
	@Test
	public void testPropertyGetterJavaProjectPackage() throws CoreException, EolRuntimeException {
		IPackageFragment[] expectedPackages = javaProject.getPackageFragments();
		List<IPackageFragment> sourcePackages = new ArrayList<IPackageFragment>();

		for (IPackageFragment packageFragment : expectedPackages) {
			// if this is a java source package
			if (packageFragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
				sourcePackages.add(packageFragment);
			}
		}
		//get expected IPackageFragments []
		expectedPackages = sourcePackages.toArray(new IPackageFragment[sourcePackages.size()]);
		
		//call PropertyGetter
		IPackageFragment[] acutualPackages = (IPackageFragment[]) model
				.getPropertyGetter().invoke(javaProject, "package");

		assertArrayEquals(expectedPackages, acutualPackages);
	}
	
	/**
	 * Tests JdtPropertyGetter.invoke(IPackageFragment, "class")
	 * 
	 * @throws CoreException
	 * @throws EolRuntimeException
	 */
	@Test
	public void testPropertyGetterIPackageFragmentClass() throws CoreException,
			EolRuntimeException {
		List<TypeDeclaration> expectedTypeList = new ArrayList<TypeDeclaration>();
		ICompilationUnit[] iUnits = packageFragment.getCompilationUnits();
		for (ICompilationUnit iUnit : iUnits) {
			CompilationUnit unit = Visitor.parse(iUnit);
			Visitor cVisitor = new Visitor();
			unit.accept(cVisitor);
			expectedTypeList.addAll(cVisitor.getClasses());
		}
		// get expected TypeDeclaration []
		TypeDeclaration[] expectedTypes = expectedTypeList
				.toArray(new TypeDeclaration[expectedTypeList.size()]);

		// call PropertyGetter
		ArrayList<TypeDeclaration> acutualTypesList = (ArrayList<TypeDeclaration>) model
				.getPropertyGetter().invoke(packageFragment, "class");
		TypeDeclaration[] actualTypes = acutualTypesList
				.toArray(new TypeDeclaration[acutualTypesList.size()]);

		// two arrays are different:
		// assertArrayEquals(expectedTypes, actualTypes);

		for (int i = 0; i < expectedTypes.length; i++) {
			// here, we compare the string format of the two type declarations
			assertEquals(expectedTypes[i].toString(), actualTypes[i].toString());
		}
	}
	
	/**
	 * Tests JdtPropertyGetter.invoke(TypeDeclaration, "method")
	 * 
	 * @throws CoreException
	 * @throws EolRuntimeException
	 */
	@Test
	public void testPropertyGetterTypeDeclarationMethod() throws CoreException,
			EolRuntimeException {
		// get expected MethodDeclaration []
		MethodDeclaration[] expectedMethods = typeDeclaration.getMethods();

		// call PropertyGetter
		MethodDeclaration[] actualMethods = (MethodDeclaration []) model
				.getPropertyGetter().invoke(typeDeclaration, "method");
		
		assertArrayEquals(expectedMethods, actualMethods);
	}
}
